﻿using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using netCoreStudy.Auth;
using netCoreStudy.Entities;
using netCoreStudy.Helper;

namespace netCoreStudy.Extensions
{
	/// <summary>
	/// 授权认证设置(jwt配置)
	/// </summary>
	public static class AuthorizeSetup
	{
		public static void AddAuthorizeSetup(
			this IServiceCollection services)
		{
			TokenEntity token = AppSettingHelper.GetWholeObj<TokenEntity>("tokenConfig");
			byte[] tokenEncoding = Encoding.ASCII.GetBytes(token.Secret);
			SymmetricSecurityKey tokenSignKey = new SymmetricSecurityKey(tokenEncoding);
			SigningCredentials signingCredentials =
				new SigningCredentials(tokenSignKey, SecurityAlgorithms.HmacSha256);
			TokenValidationParameters parameters =
				new TokenValidationParameters()
				{
					ValidateIssuerSigningKey = true, // 是否 验证密钥
					IssuerSigningKey = tokenSignKey, // 设置 密钥
					ValidateIssuer = true, // 是否验证发型人
					ValidIssuer = token.Issuer, // 设置 发行人
					ValidateAudience = true, // 是否验证订阅人
					ValidAudience = token.Audience, // 设置 订阅人
					ValidateLifetime = true, // 是否设置 过期时间  当设置exp和nbf时有效 同时启用ClockSkew 
					//注意这是缓冲过期时间，总的有效时间等于这个时间加上jwt的过期时间，如果不配置，默认是5分钟
					ClockSkew = TimeSpan.Zero,
					RequireExpirationTime = true, // 设置是否需要过期时间
				};
			// jwt 配置
			services
				// 开启认证 
				.AddAuthentication(x =>
				{
					x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
					// 替换 默认 的 401 403这种提示 ,改用自定义的提示 (200 ok 里面加内容 )
					// 使用自定义的 scheme(方案)
					x.DefaultChallengeScheme = nameof(ApiResponseHandler);
					x.DefaultForbidScheme = nameof(ApiResponseHandler);
				})
				// 注册jwt 服务 
				.AddJwtBearer(x =>
				{
					x.RequireHttpsMetadata = false;
					x.SaveToken = true;
					x.TokenValidationParameters =
						parameters;
				})
				// 替换 默认 的 401 403这种提示 ,改用自定义的提示 (200 ok 里面加内容 )
				.AddScheme<AuthenticationSchemeOptions, ApiResponseHandler>(
					nameof(ApiResponseHandler), o => { })
				;
			// 动态 权限 配置 
			List<PermissionItem> permission = new List<PermissionItem>();
			PermissionRequirement permissionRequirement = new PermissionRequirement(
				"/api/denied", // 拒绝授权的跳转地址（目前无用）
				permission,
				ClaimTypes.Role, //基于角色的授权
				token.Issuer, //发行人
				token.Audience, //听众
				signingCredentials, //签名凭据
				expiration: TimeSpan.FromSeconds(token
					.AccessExpiration) //接口的过期时间 在 appSettings.json 里面 配置 
			);

			// 自定义复杂的策略授权
			services.AddAuthorization(options =>
			{
				options.AddPolicy("Permission", policy => policy.Requirements.Add(permissionRequirement));
			});

			// 注入 http 上下文类
			services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
			// 依赖注入一个 实体类  PermissionRequirement
			services.AddSingleton(permissionRequirement);
			// 依赖注入一个 权限处理器,请求 的时候 处理下
			services.AddScoped<IAuthorizationHandler, PermissionHandler>();
		}
	}
}